﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RPGProject.Data_Structures
{
    /// <summary>
    /// A doubly linked list implementation.  Acts as a queue.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class DoubleLinkedList<T> : IEnumerable<T>
    {
        /// <summary>
        /// Size of the list.
        /// </summary>
        private int size;
        /// <summary>
        /// The first node.
        /// </summary>
        private Node head;

        public int Size
        {
            get { return this.size; }
        }
        /// <summary>
        /// Create a new linked list.
        /// </summary>
        public DoubleLinkedList()
        {
            this.size = 0;
        }
        /// <summary>
        /// Add a new set of data.
        /// </summary>
        /// <param name="data"></param>
        public void Add(T data)
        {
            if (this.head == null)
            {
                this.head = new Node(data);
                this.size++;
            }
            else
            {
                this.head.Add(data);
                this.size++;
            }
        }
        /// <summary>
        /// Removes the first element.
        /// </summary>
        /// <returns></returns>
        public T Remove()
        {
            if (size == 0)
                throw new Exception("Empty list exception.");
            T ret = this.head.Data;
            this.head = this.head.GetNext();
            this.size--;
            return ret;
        }
        /// <summary>
        /// A class Node
        /// </summary>
        public class Node
        {
            /// <summary>
            /// Data
            /// </summary>
            private T data;
            private Node next;
            private Node prev;

            public T Data
            {
                get { return this.data; }
                set { this.data = value; }
            }

            public Node(T data = default(T))
            {
                this.data = data;
                this.next = null;
                this.prev = null;
            }
            /// <summary>
            /// Adds the data to the end of the list.
            /// </summary>
            /// <param name="data"></param>
            public void Add(T data)
            {
                if (this.next == null)
                {
                    this.next = new Node(data);
                    this.next.prev = this;
                }
                else
                {
                    this.next.Add(data);
                }
            }

            public Node GetNext()
            {
                return this.next;
            }
            public Node GetPrev()
            {
                return this.prev;
            }
        }
        /// <summary>
        /// Uses yield return to iterate.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            Node current = this.head;
            while (current != null)
            {
                yield return current.Data;
                this.head = this.head.GetNext();
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new LinkedListEnumerator(this);
        }

        /// <summary>
        /// A silly implementation of an enumerator.
        /// </summary>
        public class LinkedListEnumerator : IEnumerator<T>
        {
            private bool first;
            private Node current;
            private DoubleLinkedList<T> linkedList;

            public LinkedListEnumerator(DoubleLinkedList<T> linkedList)
            {
                this.first = true;
                this.linkedList = linkedList;
            }
            public T Current
            {
                get { return this.current.Data; }
            }

            public void Dispose()
            {
            }

            object System.Collections.IEnumerator.Current
            {
                get { return this.Current; }
            }

            public bool MoveNext()
            {
                if (this.first)
                {
                    this.current = this.linkedList.head;
                }
                else
                {
                    this.current = this.current.GetNext();
                }
                return this.current == null;
            }

            public void Reset()
            {
                this.first = true;
                this.current = null;
            }
        }
    }
}
